package org.mirrentools.attendance.console.service.impl;


import org.mirrentools.attendance.console.common.*;
import org.mirrentools.attendance.console.service.SecurityHandler;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.jdbc.JDBCClient;
import io.vertx.ext.sql.assist.SQLExecute;
import io.vertx.ext.sql.assist.SqlAndParams;
import io.vertx.ext.web.RoutingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.HashMap;
import java.util.Map;

/**
 * 权限认证的默认实现
 *
 * @author <a href="http://szmirren.com">Mirren</a>
 */
public class SecurityHandlerImpl implements SecurityHandler {
  private static final Logger LOG = LogManager.getLogger(SecurityHandlerImpl.class);

  /**
   * 过滤名单
   */
  private final Map<String, Boolean> filterMap;
  /**
   * 数据库操作客户端
   */
  private final SQLExecute<JDBCClient> jdbcExecute;
  /**
   * 会话管理
   */
  private final SessionDataStore sessionDataStore;

  public SecurityHandlerImpl(Vertx vertx, Map<String, Boolean> filterMap, JDBCClient jdbcPool) {
    super();
    this.filterMap = filterMap == null ? new HashMap<>() : filterMap;
    this.jdbcExecute = SQLExecute.createJDBC(jdbcPool);
    this.sessionDataStore = SessionDataStore.instance(vertx);
  }

  @Override
  public void authToken(RoutingContext rct) {
    String path = rct.request().path();
    if (LOG.isDebugEnabled()) {
      LOG.debug("执行权限认证-->:path:" + path);
      LOG.debug("\n" + rct.request().params());
    }
    // 判断路径是否在过滤名单中
    if (filterMap.get(path) != null) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("执行权限认证-->成功:在过滤名单中...");
      }
      rct.next();
    } else {
      HttpServerResponse response = rct.response().putHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=utf-8");
      String uid = rct.request().getParam("uid");// 用户携带的id
      String utoken = rct.request().getParam("token");// 用户携带的token
      if (StringUtil.isNullOrEmpty(uid, utoken)) {
        if (LOG.isDebugEnabled()) {
          LOG.debug("执行权限认证-->失败:用户的id或者token有空");
        }
        response.end(ResultFormat.formatAsNull(ResultCodeEnum.C412).toBuffer());
        return;
      }

      // 存储在session的token
      Object stoken = sessionDataStore.get(uid, Constant.USER_LOGIN_TOKEN_KEY);
      if (LOG.isDebugEnabled()) {
        LOG.debug("执行权限认证->session token-->:" + stoken);
      }
      if (stoken != null) {
        if (utoken.equals(stoken)) {
          if (LOG.isDebugEnabled()) {
            LOG.debug("执行权限认证-->成功:从session中获取token");
          }
          rct.next();
        } else {
          response.end(ResultFormat.formatAsNull(ResultCodeEnum.C401).toBuffer());
        }
        return;
      }

      getToken(uid, rest -> {
        if (rest.succeeded()) {
          String dtoken = rest.result();
          if (dtoken != null) {
            if (utoken.equals(dtoken)) {
              if (LOG.isDebugEnabled()) {
                LOG.debug("执行权限认证-->通过!");
              }
              sessionDataStore.put(uid, Constant.USER_LOGIN_TOKEN_KEY, dtoken);
              rct.next();
            } else {
              if (LOG.isDebugEnabled()) {
                LOG.debug("执行权限认证-->不通过:token不匹配!");
              }
              response.end(ResultFormat.formatAsNull(ResultCodeEnum.C401).toBuffer());
            }
          } else {
            if (LOG.isDebugEnabled()) {
              LOG.debug("执行权限认证-->不通过:token为空!");
            }
            response.end(ResultFormat.formatAsNull(ResultCodeEnum.C401).toBuffer());
          }
        } else {
          response.end(ResultFormat.formatAsNull(ResultCodeEnum.C500).toBuffer());
        }
      });
    }
  }

  @Override
  public void getToken(String userId, Handler<AsyncResult<String>> handler) {
    String sql = "SELECT token FROM users WHERE id=?";
    jdbcExecute.queryAsObj(new SqlAndParams(sql, new JsonArray().add(userId)), res -> {
      if (res.succeeded()) {
        JsonObject result = res.result() == null ? new JsonObject() : res.result();
        if (LOG.isDebugEnabled()) {
          LOG.debug("执行权限认证获取用户token-->结果:" + result);
        }
        String token = result.getString("token");
        handler.handle(Future.succeededFuture(token));
      } else {
        LOG.error("执行权限认证获取用户token-->失败:", res.cause());
        handler.handle(Future.failedFuture(res.cause()));
      }
    });
  }
}
